package cc.ppay.sso.client.filter;

import cc.ppay.sso.client.service.SsoClientService;
import cc.ppay.sso.common.constant.SsoConstant;
import cc.ppay.sso.common.model.CommonResult;
import cc.ppay.sso.common.model.SsoUser;
import cc.ppay.sso.common.path.impl.AntPathMatcher;
import cc.ppay.sso.common.util.CookieUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 拦截所有web cookie认证的请求
 *
 * @author Ade.Xiao 2021/1/29 15:43
 */
@Slf4j
public class SsoWebFilter extends HttpServlet implements Filter {
    private static final long serialVersionUID = -3822641769324963955L;

    private static final AntPathMatcher antPathMatcher = new AntPathMatcher();

    private String ssoServer;
    private String logoutPath;
    private String excludedPaths;
    private SsoClientService clientService;

    /**
     * 构造函数
     *
     * @param ssoServer     统一认证地址
     * @param logoutPath    登出地址
     * @param excludedPaths 不拦截路径，多个用英文逗号分隔
     * @param clientService 客户端逻辑实现
     * @author Ade.Xiao 2021/1/30 16:38
     */
    public SsoWebFilter(String ssoServer, String logoutPath, String excludedPaths, SsoClientService clientService) {
        this.ssoServer = ssoServer;
        this.logoutPath = logoutPath;
        this.excludedPaths = excludedPaths;
        this.clientService = clientService;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        // make url
        String servletPath = req.getServletPath();

        // excluded path check
        if (excludedPaths != null && excludedPaths.trim().length() > 0) {
            for (String excludedPath : excludedPaths.split(",")) {
                String uriPattern = excludedPath.trim();

                // 支持ANT表达式
                if (antPathMatcher.match(uriPattern, servletPath)) {
                    // excluded path, allow
                    chain.doFilter(request, response);
                    return;
                }

            }
        }

        // logout path check
        if (logoutPath != null && logoutPath.trim().length() > 0 && logoutPath.equals(servletPath)) {

            // remove cookie
            CookieUtil.remove(req, res, SsoConstant.SSO_SESSIONID);

            // redirect logout
            String logoutPageUrl = ssoServer.concat(SsoConstant.SSO_LOGOUT);
            res.sendRedirect(logoutPageUrl);

            return;
        }

        // valid login user, cookie + redirect
        String sessionid = req.getHeader(SsoConstant.SSO_SESSIONID);
        if (StrUtil.isBlank(sessionid)) {
            sessionid = req.getParameter(SsoConstant.SSO_SESSIONID);
        }
        SsoUser ssoUser = clientService.loginCheck(SsoConstant.AUTH_TYPE_WEB, sessionid);

        // valid login fail
        if (ssoUser == null) {

            String header = req.getHeader("content-type");
            boolean isJson = header != null && header.contains("json");
            if (isJson) {

                // json msg
                res.setContentType("application/json;charset=utf-8");
                res.getWriter().println(JSONUtil.toJsonStr(new CommonResult(SsoConstant.SSO_LOGIN_FAIL_RESULT.getCode(), SsoConstant.SSO_LOGIN_FAIL_RESULT.getMsg())));

                return;
            } else {
                // total link
                String link = req.getRequestURL().toString();
                // redirect logout
                String loginPageUrl = ssoServer.concat(SsoConstant.SSO_LOGIN) + "?" + SsoConstant.REDIRECT_URL + "=" + link;

                res.sendRedirect(loginPageUrl);
                return;
            }

        }

        // ser sso user
        request.setAttribute(SsoConstant.SSO_USER, ssoUser);
        // already login, allow
        chain.doFilter(request, response);
        return;
    }

}
